package net.keebook.webapp.bean;

import java.io.IOException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.TimeZone;

import javax.annotation.PostConstruct;
import javax.faces.application.FacesMessage;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.RequestScoped;
import javax.faces.component.UIInput;
import javax.faces.context.FacesContext;
import javax.inject.Inject;

import net.keebook.webapp.dao.GroupDao;
import net.keebook.webapp.dao.KeeBookDao;
import net.keebook.webapp.dao.PasswordDao;
import net.keebook.webapp.dao.SettingDao;
import net.keebook.webapp.entity.ItemGroup;
import net.keebook.webapp.entity.KeeBook;
import net.keebook.webapp.entity.PasswordItem;
import net.keebook.webapp.model.SessionModel;
import net.keebook.webapp.util.AccessRight;
import net.keebook.webapp.util.AesCrypto;
import net.keebook.webapp.util.DateUtil;
import net.keebook.webapp.util.MessageBundle;
import net.keebook.webapp.util.Messages;
import net.keebook.webapp.util.SettingId;

import org.apache.commons.fileupload.FileUploadException;
import org.apache.log4j.Logger;
import org.apache.myfaces.custom.fileupload.UploadedFile;


import pl.sind.keepass.exceptions.KeePassDataBaseException;
import pl.sind.keepass.exceptions.UnsupportedDataBaseException;
import pl.sind.keepass.kdb.KeePassDataBase;
import pl.sind.keepass.kdb.KeePassDataBaseManager;
import pl.sind.keepass.kdb.v1.Entry;
import pl.sind.keepass.kdb.v1.Group;
import pl.sind.keepass.kdb.v1.IdField;
import pl.sind.keepass.kdb.v1.KeePassDataBaseV1;

@ManagedBean
@RequestScoped
public class ImportBean {
	private String fileFormat;
	private Boolean importGroups;
	private Boolean maintainGroups;
	private String password;
	private String encryptionPassword;
	private UploadedFile file;
	@Inject
	private GroupDao groupDao;
	@Inject
	private PasswordDao passwordDao;
	@Inject
	private SettingDao settingDao;
	@Inject
	private KeeBookDao keeBookDao;
	private SessionModel sessionModel;
	
	@PostConstruct
	public void init() throws IOException {
		setSessionModel(SessionModel.getInstance(FacesContext.getCurrentInstance()));
		
		if (!getKeeBookDao().getKeeBookAccess(getSessionModel(), AccessRight.READ_WRITE))  {
			Logger.getLogger(this.getClass()).warn("Forbidden access to bean from userId="+getSessionModel().getUserId());
			FacesContext.getCurrentInstance().getExternalContext().redirect("403.xhtml");
			return;
		}
	}
	
	private boolean isBackupGroup(List<Group> groups, IdField id) {
		for (Group g : groups) {
			if (g.getGroupId().getId() == id.getId()) {
				if ("Backup".equals(g.getGroupName().getText())) {
					return true;
				}
			}
		}
		return false;
	}
	
	public void process() throws IOException, FileUploadException {
		Logger.getLogger(this.getClass()).debug("Incoming import request from userId="+getSessionModel().getUserId());
		
		if (!getKeeBookDao().getKeeBookAccess(getSessionModel(), AccessRight.READ_WRITE))  {
			Logger.getLogger(this.getClass()).warn("Forbidden access to bean from userId="+getSessionModel().getUserId());
			FacesContext.getCurrentInstance().getExternalContext().redirect("403.xhtml");
			return;
		}
		
		KeePassDataBase keePassDb = null;
		try {
			Logger.getLogger(this.getClass()).debug("Opening keepass database for userId="+getSessionModel().getUserId());
			keePassDb = KeePassDataBaseManager.openDataBase(getFile().getInputStream(), null, getPassword());
			Logger.getLogger(this.getClass()).debug("Keepass database successfully opened for userId="+getSessionModel().getUserId());
		} catch (UnsupportedDataBaseException e) {
			FacesContext context = FacesContext.getCurrentInstance();
			UIInput component = (UIInput)context.getViewRoot().findComponent("importForm:file");
			component.setValid(false);
			context.addMessage(component.getClientId(context), new FacesMessage(MessageBundle.get().getString("error.fileFormatUnsupported")));
			Logger.getLogger(this.getClass()).warn("UnsupportedDataBaseException for userId="+getSessionModel().getUserId(), e);
			return;
		} catch (KeePassDataBaseException e) {
			FacesContext context = FacesContext.getCurrentInstance();
			UIInput component = (UIInput)context.getViewRoot().findComponent("importForm:file");
			component.setValid(false);
			context.addMessage(component.getClientId(context), new FacesMessage(MessageBundle.get().getString("error.errorDecrypting")));
			
			Logger.getLogger(this.getClass()).warn("KeePassDataBaseException for userId="+getSessionModel().getUserId(), e);
			return;
		}			
		
		KeePassDataBaseV1 kdb = (KeePassDataBaseV1)keePassDb;
		KeeBook keeBook = getKeeBookDao().get(getSessionModel().getKeeBookId());
		Logger.getLogger(this.getClass()).debug("Target keeboodId="+keeBook.getId()+" for userId="+getSessionModel().getUserId());
		
		List<Group> groups = kdb.getGroups();
		Logger.getLogger(this.getClass()).debug("Found "+groups.size()+" groups in keepass database for userId="+getSessionModel().getUserId());
		
		Map<Integer, ItemGroup> groupMaintenance = new HashMap<Integer, ItemGroup>();
		if (getImportGroups()) {
			for (Group g : groups) {
				if ("Backup".equals(g.getGroupName().getText())) {
					continue;
				}
				ItemGroup ig = new ItemGroup();
				ig.setKeeBook(keeBook);
				ig.setTitle(g.getGroupName().getText());
				getGroupDao().store(ig);
				groupMaintenance.put(g.getGroupId().getId(), ig);
			}
		}
		
		List<Entry> entries = kdb.getEntries();
		Logger.getLogger(this.getClass()).debug("Found "+entries.size()+" entries in keepass database for userId="+getSessionModel().getUserId());
		for (Entry e : entries) {			
			if ("Meta-Info".equals(e.getTitle().getText())) {
				continue;
			}
			if (isBackupGroup(groups, e.getGroupId())) {
				continue;
			}
			
			PasswordItem item = new PasswordItem();
			item.setCreationTime(DateUtil.getUtcTimestamp(TimeZone.getTimeZone(getSettingDao().get(SettingId.SYSTEM_TIMEZONE).getValue())));
			item.setLastModification(DateUtil.getUtcTimestamp(TimeZone.getTimeZone(getSettingDao().get(SettingId.SYSTEM_TIMEZONE).getValue())));
			item.setNotes(encrypt(e.getNotes().getText()));
			item.setPassword(encrypt(e.getPassword().getText()));
			item.setTitle(e.getTitle().getText());
			item.setUrl(encrypt(e.getUrl().getText()));
			item.setUsername(encrypt(e.getUsername().getText()));
			item.setKeeBook(keeBook);
			
			if (getImportGroups() && getMaintainGroups()) {
				ItemGroup itemGroup = groupMaintenance.get(e.getGroupId().getId());
				item.setGroup(itemGroup);
			}
			
			getPasswordDao().store(item);
		}
		
		Logger.getLogger(this.getClass()).debug("Import completed for userId="+getSessionModel().getUserId());
		FacesContext.getCurrentInstance().getExternalContext().redirect("import.xhtml?msg="+Messages.IMPORT_COMPLETED);
	}
	
	private String encrypt(String s) {
		if (s == null || s.trim().isEmpty()) {
			return "";
		}
		try {
			return AesCrypto.encrypt(s.trim(), getEncryptionPassword());
		} catch (Exception e) {
			return "";
		}
	}

	public String getFileFormat() {
		return fileFormat;
	}

	public void setFileFormat(String fileFormat) {
		this.fileFormat = fileFormat;
	}

	public Boolean getImportGroups() {
		return importGroups;
	}

	public void setImportGroups(Boolean importGroups) {
		this.importGroups = importGroups;
	}

	public Boolean getMaintainGroups() {
		return maintainGroups;
	}

	public void setMaintainGroups(Boolean maintainGroups) {
		this.maintainGroups = maintainGroups;
	}

	public String getPassword() {
		return password;
	}

	public void setPassword(String password) {
		this.password = password;
	}

	public UploadedFile getFile() {
		return file;
	}

	public void setFile(UploadedFile file) {
		this.file = file;
	}

	public GroupDao getGroupDao() {
		return groupDao;
	}

	public void setGroupDao(GroupDao groupDao) {
		this.groupDao = groupDao;
	}

	public PasswordDao getPasswordDao() {
		return passwordDao;
	}

	public void setPasswordDao(PasswordDao passwordDao) {
		this.passwordDao = passwordDao;
	}

	public String getEncryptionPassword() {
		return encryptionPassword;
	}

	public void setEncryptionPassword(String encryptionPassword) {
		this.encryptionPassword = encryptionPassword;
	}

	public SettingDao getSettingDao() {
		return settingDao;
	}

	public void setSettingDao(SettingDao settingDao) {
		this.settingDao = settingDao;
	}

	public SessionModel getSessionModel() {
		return sessionModel;
	}

	public void setSessionModel(SessionModel sessionModel) {
		this.sessionModel = sessionModel;
	}

	public KeeBookDao getKeeBookDao() {
		return keeBookDao;
	}

	public void setKeeBookDao(KeeBookDao keeBookDao) {
		this.keeBookDao = keeBookDao;
	}
	
}
